package study.saas.common.util;


import study.saas.common.cache.JedisService;
import study.saas.common.dto.RedisLockDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;

import java.util.Collections;
import java.util.List;


/**
 * @author maple
 * @Title: RedisLock
 * @Description: redis 锁
 * @date 2019/7/08
 */
public class RedisLock {
    private static final Logger log = LoggerFactory.getLogger(RedisLock.class);
    private static JedisService jedisService = SpringUtil.getBean(JedisService.class);
    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";
    private static final Long RELEASE_SUCCESS = 1L;

    public static final int SECOND = 1000;

    /**
     * 尝试获取分布式锁
     *
     * @param lockKey    锁
     * @param requestId  请求标识
     * @param expireTime 超期时间
     * @return 是否获取成功
     */
    public static boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime) {
        String result = jedisService.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
        return LOCK_SUCCESS.equals(result);

    }

    /**
     * 释放分布式锁
     *
     * @param lockKey   锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public static boolean releaseDistributedLock(String lockKey, String requestId) {
        //Lua脚本 实现原子操作
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedisService.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));

        return RELEASE_SUCCESS.equals(result);
    }

    /**
     * 尝试获取分布式锁
     *
     * @param lockKey    锁
     * @param requestId  请求标识
     * @param expireTime 超期时间
     * @return 是否获取成功
     */
    public static boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime, Jedis jedis) {
        String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
        return LOCK_SUCCESS.equals(result);

    }

    /**
     * 释放分布式锁
     *
     * @param lockKey   锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public static boolean releaseDistributedLock(String lockKey, String requestId, Jedis jedis) {
        //Lua脚本 实现原子操作
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));

        return RELEASE_SUCCESS.equals(result);
    }

    /**
     * 尝试获取多个分布式锁
     *
     * @param redisLockDTOList redis锁dto 列表
     * @return 是否获取成功
     */
    public static boolean tryGetDistributedLockList(List<RedisLockDTO> redisLockDTOList, Jedis jedis) throws InterruptedException {
        for (RedisLockDTO redisLockDTO : redisLockDTOList) {
            log.info("开始获取redis,key:{}", redisLockDTO.getLockKey());
            //TODO CompletableFuture
            long time = System.currentTimeMillis();
            boolean success = false;
            while (!LOCK_SUCCESS.equals(jedis.set(redisLockDTO.getLockKey(), redisLockDTO.getRequestId(), SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, redisLockDTO.getExpireTime()))) {
                log.info("失败获取redis,key:{}", redisLockDTO.getLockKey());
                for (int i = 0; i < 10; i++) {
                    if (LOCK_SUCCESS.equals(jedis.set(redisLockDTO.getLockKey(), redisLockDTO.getRequestId(), SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, redisLockDTO.getExpireTime()))) {
                        success = true;
                        break;
                    }
                }
                //如果成功,获取下个锁,如果失败,再去获取这个锁
                if (success) {
                    break;
                } else {
                    Thread.sleep(40);
                }
            }
        }
        return true;
    }

    /**
     * 释放分布式锁
     *
     * @param redisLockDTOList redis锁dto 列表
     * @return 是否释放成功
     */
    public static boolean releaseDistributedLockList(List<RedisLockDTO> redisLockDTOList, Jedis jedis) {
        //Lua脚本 实现原子操作
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        log.info("开始释放锁:go");
        for (RedisLockDTO redisLockDTO : redisLockDTOList) {
            long time = System.currentTimeMillis();
            Object result = jedis.eval(script, Collections.singletonList(redisLockDTO.getLockKey()), Collections.singletonList(redisLockDTO.getRequestId()));
            if (!RELEASE_SUCCESS.equals(result)) {
                log.info("失败释放redis,key:{}", redisLockDTO.getLockKey());
            }
        }
        return true;
    }

}
